#include "ListAction.h"
#include "Common/Exceptions/NullPointerException.h"
#include "Common/Exceptions/ProductException.h"

using namespace cmn;

namespace editor_core {

	ListAction::ListAction() :
		m_listIsFinalized( false )
	{}

	ListAction::~ListAction() {
		for( IteratorPtr<IAction*> actionsIter( m_list ); !actionsIter.IsEnd(); actionsIter.Next() ) {
			delete actionsIter.Current();
		}
	}

	ActionType ListAction::GetType() const {
		if( m_list.GetLength() == 0 ) return ActionType::Elementary;
		for( IteratorPtr<IAction*> actionsIter( m_list ); !actionsIter.IsEnd(); actionsIter.Next() ) {
			IAction* action = actionsIter.Current();
			if( action->GetType() == ActionType::Undoable )
				return ActionType::Undoable;
		}
		return ActionType::Elementary;
	}

	void ListAction::AddAction( IAction* action ) {
		if( !action )
			throw NullPointerException( "Action is NULL" );

		if( m_listIsFinalized )
			throw ProductException( "ActionList is complete. You cannot add anything in it." );

		m_list.Add( action );
	}

	void ListAction::RemoveAction( IAction* action ) {
		if( !action )
			throw NullPointerException( "Action is NULL" );

		if( m_listIsFinalized )
			throw ProductException( "ActionList is complete. You cannot remove anything from it." );

		m_list.Remove( action );
	}

	void ListAction::GetActions( cmn::ICollection<IAction*>& actions ) const {
		for( IteratorPtr<IAction*> actionsIter( m_list ); !actionsIter.IsEnd(); actionsIter.Next() ) {
			actions.Add( actionsIter.Current() );
		}
	}

	bool ListAction::Commit() {
		m_listIsFinalized = true;

		int commitedCount = TryCommit();
		if( commitedCount != m_list.GetLength() ) {
			RollbackFrom( commitedCount );
			return false;
		}
		return true;
	}

	int ListAction::TryCommit() {
		for( int i = 0; i < m_list.GetLength(); ++i ) {
			if( !m_list[i]->Do() ) return i;
		}
		return m_list.GetLength();
	}

	void ListAction::RollbackFrom( int index ) {
		for( int i = index - 1; i >= 0; --i ) {
			m_list[i]->Undo();
		}
	}

	void ListAction::Rollback() {
		RollbackFrom( m_list.GetLength() );
	}

	void ListAction::Recommit() {
		int recommited = TryCommit();
		if( recommited != m_list.GetLength() )
			throw ProductException( "We cannot recommit previously commited action list. Check Action " + ( recommited - 1 ) );
	}

}
